iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0

在前面幾個章節中,應該或多或少都有看到函式在 TypeScript 中的寫法,它比 JavaScript 多了參數和返回值的型別設定,我們看一下函式基本的宣告方式:

函式的宣告

宣告函式有下面種方式,如果函式沒有返回值的話,我們可以使用 void 型別,在 TypeScript 中的寫法為:

  • 傳統函式 (具名函式):
function greet(name: string): void {
  console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
}

greet("威爾豬");
  • 箭頭函式:
const greet = (name: string): void => {
  console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
};

greet("威爾豬");
  • 匿名函式:
const greet = function (name: string): void {
  console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
};

greet("威爾豬");
  • 立即函式 (IIFE):
(function (): void {
  let name: string = "威爾豬";
  console.log(`哈囉,${name}!`); // 輸出: 哈囉,威爾豬!
})();

至於要用哪一種寫法,就看會不會使用到 this,而 this 的指向在不同的情況下可能會有所不同,這取決於函式是如何被調用,相信被 JavaScript 荼毒過的人,對於 this 指向應該是一段痛苦的回憶。

this 指向

  • 全域中的 this在全域範圍中 (沒有包在任何物件或函式內),this 通常指向全域物件,例如 Window (瀏覽器環境) 或 global (Node.js 環境)。
console.log(this); // this 會指向瀏覽器中的 Window 物件或 Node.js 的 global
  • 物件方法中的 this:當一個函式被調用為物件的方法時,this 通常指向調用這個方法的物件
const obj = {
  name: "威爾豬",
  greet() {
    console.log(`哈囉,${this.name}!`); // this 會指向 obj 物件
  },
};

obj.greet(); // 輸出: 哈囉,威爾豬!
  • 箭頭函式的 this箭頭函式沒有自己的 this,不會改變 this 的指向。
const obj = {
  name: "威爾豬",
  greet: () => {
    console.log(`哈囉,${this.name}!`); // this 會指向瀏覽器中的 Window 物件
  },
};

obj.greet(); // 輸出: 哈囉, !

TypeScript 也會提醒我們 this 指向全域。

https://ithelp.ithome.com.tw/upload/images/20230916/20141250IrBhmVX7Pt.png

  • 類別中的 this:在 class 中,this 通常指向類別的實體,可以在類別的方法中使用。
class MyClass {
  constructor(public name: string) {
    this.name = name;
  }

  greet() {
    console.log(`哈囉,${this.name}!`); // this 指向類別 MyClass
  }
}

const myClass = new MyClass("威爾豬");
myClass.greet(); // 輸出: 哈囉,威爾豬!
  • 使用 call 或 apply 方法來改變 this:我們可以使用 call()apply() 來改變指定函式的 this。兩者的差異為 call 傳入的參數用逗號隔開,而 apply 的參數則是使用陣列的形式。
interface IPerson {
  name: string;
}

function greet(this: IPerson) {
  console.log(`哈囉,${this.name}!`); // 輸出: 哈囉,威爾豬!
}

const person: IPerson = { name: "威爾豬" };

greet.call(person); // this 會改變指向 person 物件

注意:使用 call 或 apply 方法請使用傳統函式,不要用箭頭函式,以避免運行出現非預期的效果。

函式的參數

函式可以接受一個或多個參數,在 TypeScript 中,函式宣告中使用的參數名稱,必須事先定義型別,以便在函式內部使用。

const add = (a: number, b: number): number => a + b;
console.log(add(2, 3)); // 輸出: 5

預設參數值

在 JavaScript 我們可以為函式的參數提供預設值,這樣在呼叫函式時如果未提供相應的參數,則會使用預設值,而在 TypeScript 的寫法為:

const greet = (name: string = "陌生人"): void => {
  console.log(`哈囉,${name}!`);
};

greet(); // 輸出: 哈囉,陌生人!
greet("威爾豬"); // 輸出: 哈囉,威爾豬!

函式重載

函式重載是指 定義多個具有相同名稱但具有不同參數類型或不同返回類型的函式,在處理不同類型的輸入、不同參數個數或型別組合時非常有用,我們可以為同一個函式定義多種不同的參數組合和返回值型別。

function userInfo(name: string): void;
function userInfo(name: string, age: number): void;
function userInfo(name: string, age?: number): void {
  if (age !== undefined) {
    console.log(`姓名: ${name},年齡: ${age}`);
  } else {
    console.log(`姓名: ${name}`);
  }
}

userInfo("威爾豬"); // 輸出: 姓名: 威爾豬
userInfo("威爾豬", 3); // 輸出: 姓名: 威爾豬,年齡: 3

在這個例子中,我們定義了一個 userInfo 函式的重載。第一個重載接受一個 name 參數,第二個重載接受 name 和 age 兩個參數。實際的函式實現在最後一個定義中,根據參數的數量進行不同的處理。

再來看另外一個例子:假設我們可能需要一個函式來計算圖形面積,並且根據形狀的不同提供不一樣的計算方式:

function handleArea(shape: "circle", radius: number): number;
function handleArea(shape: "rect", width: number, height: number): number;
function handleArea(shape: string, ...args: number[]): number {
  if (shape === "circle") {
    const [radius] = args;
    return Math.round(Math.PI * radius ** 2 * 100) / 100;
  } else if (shape === "rect") {
    const [width, height] = args;
    return width * height;
  } else {
    throw new Error("沒有這個圖形!");
  }
}

console.log(handleArea("circle", 10)); // 輸出: 314.16
console.log(handleArea("rect", 4, 6)); // 輸出: 24

在這個例子中,我們定義了一個 handleArea 函式,一個用於計算圓形的面積,另一個用於計算矩形的面積。實際的函式實現使用 ...args 的數字陣列來接受可變數量的參數,再根據 shape 參數的值進行相應的計算。

當然我們也可以使用其它方式來代替函式重載,範例如下:

// 使用函式重載

function greet(name: string): string;
function greet(age: number): number;
function greet(nameOrAge: string | number): string | number {
  if (typeof nameOrAge === "string") {
    return `哈囉,${nameOrAge}!`;
  } else {
    return nameOrAge * 2;
  }
}

console.log(greet("威爾豬")); // 輸出: 哈囉,威爾豬!
console.log(greet(3)); // 輸出: 6。
// 使用類型別名

type TGreet<T> = (nameOrAge: T) => T;

const greet: TGreet<string | number> = (nameOrAge) =>
  typeof nameOrAge === "string" ? `哈囉,${nameOrAge}!` : nameOrAge * 2;

console.log(greet("威爾豬")); // 輸出: 哈囉,威爾豬!
console.log(greet(3)); // 輸出: 6

函式重載只是 TypeScript 中多態性的一種形式,還有其它方式可以實現類似的效果,簡單來說,合理運用函式和型別,以及注意 this 的指向,我們可以撰寫出更具靈活性和可維護性的程式碼。


上一篇
JSON to TS (Vscode 套件)
下一篇
void VS. never
系列文
用不到 30 天學會基本 TypeScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言